查看原文
其他

原创 | Shiro-550反序列化漏洞分析

Samny SecIN技术平台 2022-06-18
点击上方蓝字 关注我吧

概述


Shiro反序列化漏洞目前为止有两个,Shiro-550(Apache  Shiro < 1.2.5)和Shiro-721( Apache  Shiro < 1.4.2 )。
这两个漏洞主要区别在于Shiro550使用已知密钥撞,后者Shiro721是使用登录后rememberMe={value}去爆破正确的key值进而反序列化,对比Shiro550条件只要有足够密钥库(条件比较低)、Shiro721需要登录(要求比较高鸡肋)。


· Apache Shiro < 1.4.2默认使用AES/CBC/PKCS5Padding模式
· Apache Shiro >= 1.4.2默认使用AES/GCM/PKCS5Padding模式


环境搭建


采用Maven仓库的形式,源码放在GitHub上,直接用Idea打开即可。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
<groupId>org.example</groupId> <artifactId>shiro-deser</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!-- hutool是一款十分强大工具库--> <!-- 官网地址 https://www.hutool.cn/--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.5.7</version> </dependency> <!-- 添加commons-collections依赖作为 payload--> <!-- <dependency>--> <!-- <groupId>commons-collections</groupId>--> <!-- <artifactId>commons-collections</artifactId>--> <!-- <version>4.0</version>--> <!-- </dependency>--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> // debug参数 <jvmArguments> -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 </jvmArguments> </configuration> </plugin> </plugins> </build> </project>
流程分析

调用org\apache\shiro\mgt\DefaultSecurityManager.class#resolvePrincipals方法获取remember凭证



DefaultSecurityManager.class#getRememberedIdentity调用方法获取rememberMe认证的序列化数据



接着调用父类org\apache\shiro\mgt\AbstractRememberMeManager.class#getRememberedPrincipals方法在122行调用getRememberedSerializedIdentity方法获取cookie中的值


然后来到org\apache\shiro\web\mgt\CookieRememberMeManager.class#getRememberedSerializedIdentity获取cookie值之后,先判断一下是否为空和deleteMe,解之Base64解码最后在95行处返回byte[]值



org\apache\shiro\mgt\AbstractRememberMeManager.class#getRememberedPrincipals方法的124行进行类型转化,类型转化的过程中会进行AES解密操作,进而作为反序列化的数据


AbstractRememberMeManager.class#convertBytesToPrincipals进行AES解密操作,最后调用反序列化方法,将数据反序列化,导致反序列化漏洞

AbstractRememberMeManager#decrypt方法
protected byte[] decrypt(byte[] encrypted) { byte[] serialized = encrypted; CipherService cipherService = this.getCipherService(); if (cipherService != null) { ByteSource byteSource = cipherService.decrypt(encrypted, this.getDecryptionCipherKey()); serialized = byteSource.getBytes(); } return serialized; }

查看bytes数据值,可以看到解密后是生成的恶意payload



完整的payload演示效果


Shiro's key爆破方式


基于原生shiro框架检测方式


l1nk3r师傅的检测思路地址: https://mp.weixin.qq.com/s/do88_4Td1CSeKLmFqhGCuQ

关键代码
SimplePrincipalCollection simplePrincipalCollection = new SimplePrincipalCollection(); ObjectOutputStream obj = new ObjectOutputStream(new FileOutputStream("payload")); obj.writeObject(simplePrincipalCollection); obj.close();


实现具体代码
public static void main(String[] args) throws IOException { // 正确key String realkey = "kPH+bIxk5D2deZiIxcaaaA=="; // 错误key String errorkey = "2AvVhdsgUs0FSA3SDFAdag=="; // 序列化文件路径 String filepath = "E:\\Soures\\JavaLearnVulnerability\\shiro\\shiro-deser\\key"; SimplePrincipalCollection simplePrincipalCollection = new SimplePrincipalCollection(); ObjectOutputStream obj = new ObjectOutputStream(new FileOutputStream(filepath)); try { // 写入序列化数据 obj.writeObject(simplePrincipalCollection); obj.close(); } catch (IOException e) { e.printStackTrace(); } FileReader fileReader = new FileReader(filepath); CbcEncrypt cbcEncrypt = new CbcEncrypt(); String realcookie = "rememberMe=" + cbcEncrypt.encrypt(realkey,fileReader.readBytes()); String errorcookie = "rememberMe=" + cbcEncrypt.encrypt(errorkey,fileReader.readBytes()); System.out.println("realcookie --> " + realcookie); System.out.println("errorcookie --> " + errorcookie); String url = "http://127.0.0.1:8001/index"; // 发送请求包,获取返回包 HttpResponse realresponse = HttpRequest.get(url).cookie(realcookie).execute(); HttpResponse errorresponse = HttpRequest.get(url).cookie(errorcookie).execute(); String result1 = realresponse.header(Header.SET_COOKIE); String result2 = errorresponse.header(Header.SET_COOKIE); // 输出结果 System.out.println("realkey ---> " + result1); System.out.println("errorkey ---> " + result2); }



总结


Gadget Chian 如下。简单来说流程就是将生成恶意Payload进行AES加密,然后Base64编码,然后以**rememberMe={value}形式发送给服务器。服务器将valueBase64解码,然后将解码后数据进行AES解密,最后反序列化执行命令。

Shiro721在登录之后,用登录后服务器生成rememberMe的值进行Base64解码之后,用解码数据,再通过Padding Oracle Attack进行爆破得到key具体参考Shiro 组件漏洞与攻击链分析
* Gadget chian: * DefaultSecurityManager.resolvePrincipals() * DefaultSecurityManager.getRememberedIdentity() * AbstractRememberMeManager.getRememberedPrincipals() * CookieRememberMeManager#getRememberedSerializedIdentity() * AbstractRememberMeManager#getRememberedPrincipals() * AbstractRememberMeManager.convertBytesToPrincipals() * AbstractRememberMeManager.decrypt() * AbstractRememberMeManager.deserialize() * ..................... * .......... * *


Shiro实用工具推荐


shiro_attack


推荐理由:javafx写的UI,支持tomcat全版本回显和Spring Boot回显。使用**SimplePrincipalCollection爆破key,支持高版本加密方式爆破(GCM模式)项目还在维护。



BurpShiroPassiveScan是一款burp插件,被动式扫描,自动识别是否为shiro框架,支持CBC/GCM两种加密方式,同时默认使用SimplePrincipalCollection爆破key,项目在维护。



踩坑记录


编码不一致问题


由于Windows cmd的编码是gdk,导致读取cmd内容的时候会aced0005变成efbfbdefbfbd**,导致无法反序列化。



解决办法将生成的payload导入文件之中,然后读取二进制数据



课外知识补充


springboot debug技巧


在配置中VM options 输入-Xms512m -Xmx512m -Xmn164m -XX:MaxPermSize=250m -XX:ReservedCodeCacheSize=64m -Dserver.port=8001 -ea

同时在配置文件pom.xml加入
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <jvmArguments> -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 </jvmArguments> </configuration> </plugin>


VScode添加插件


Hex Editor插件

效果如下


Git 自带 xxd工具

将工具路径加入环境变量



效果如下:



参考


https://issues.apache.org/jira/browse/SHIRO-550


https://paper.seebug.org/1378


https://aresx.com/2020/10/26/Shiro%E9%AB%98%E7%89%88%E6%9C%AC%E5%8A%A0%E5%AF%86%E6%96%B9%E5%BC%8F%E4%B8%8B%E7%9A%84%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/


https://mp.weixin.qq.com/s/do88_4Td1CSeKLmFqhGCuQ


往期推荐

驱动病毒那些事(一)——基础

驱动病毒那些事(二)——回调

一次金融行业的红蓝对抗总结



你要的分享、在看与点赞都在这儿~

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存